package net.apps.core;

import java.util.HashMap;

import net.apps.core.impl.ApplManagerStdImpl;
import net.apps.core.impl.ApplManagerRawImpl;
import net.apps.core.meta.Meta;

public final class ApplFactory {
	
	private static int protocols_id_counter = 0;
	private static final HashMap<String, MetaAndProtoClass> protocols = new HashMap<String, MetaAndProtoClass>();
	
	private static class MetaAndProtoClass {
		final int id;
		final String name;
		final Meta meta;
		final Class<? extends Protocol> clazz;
		public MetaAndProtoClass(int id, String name, Meta meta, Class<? extends Protocol> clazz) {
			this.id = id;
			this.name = name;
			this.meta = meta;
			this.clazz = clazz;
		}
	}

	public static ApplManagerStd makeStdApplManager() {
		return new ApplManagerStdImpl();
	}
	
	public static ApplManagerRaw makeRawApplManager() {
		return new ApplManagerRawImpl();
	}
	
	public static synchronized int allocateProtocolId(String name) {
		if (protocols.get(name) != null)
			throw new IllegalStateException("Protocol '"+name+"' already registered");
		int id = ++protocols_id_counter;
		protocols.put(name, new MetaAndProtoClass(id, name, null, null));
		return id;
	}

	public static synchronized void registerProtocol(Meta meta) {
		MetaAndProtoClass mapc = protocols.get(meta.getName());
		if (mapc == null)
			throw new IllegalStateException("Protocol id for '"+meta.getName()+"' is not allocated yet");
		if (mapc.meta == meta)
			return;
		if (!mapc.name.equals(meta.getName()) || mapc.id != meta.getId())
			throw new IllegalStateException("Missmatch name or id for protocol");
		if (mapc.meta != null)
			throw new IllegalStateException("Another protocol for '"+meta.getName()+"' is already registered");
		if (!(meta.getValue() instanceof Class) || !Protocol.class.isAssignableFrom((Class<?>)meta.getValue()))
			throw new IllegalStateException("Protocol meta getValue() must return an implementation class");
		@SuppressWarnings("unchecked")
		Class<? extends Protocol> clazz = (Class<? extends Protocol>)meta.getValue();
		protocols.put(meta.getName(), new MetaAndProtoClass(mapc.id, mapc.name, meta, clazz));
	}

	public static Protocol makeProtocol(String name) {
		MetaAndProtoClass mapc =  protocols.get(name);
		if (mapc == null)
			return null;
		try {
			return mapc.clazz.getConstructor(Meta.class).newInstance(mapc.meta);
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}
	}
}
